/**
 * 抽象类
 * 抽象类的好处呢就是可以抽离出一些事物的共性，这样就有利于代码的复用和扩展
 * 另外呢抽象类也可以实现多态
 *  */

abstract class Animal {
  // 在抽象类中呢我们可以定义一个方法它可以有具体的实现这样子类就不用实现了，这就实现了方法的复用。
  eat() {
    console.log("eat");
  }
  // 在抽象类中呢也可以不指定方法的具体实现，这就构成了一个抽象方法
  // 抽象方法的好处是：你明确知道此类可以有其它的实现那么就没必要在父类中实现了
  abstract sleep(): void;
}

// let animal = new Animal();// 可以看到无法创建抽象类的实例，所有它只能被继承

// 用 Dog 类继承 Animal
class Dog extends Animal {
  constructor(name: string) {
    super(); // 同样我们要调用下 super
    this.name = name;
  }
  name: string;
  run() {}
  // 那么我们在子类中实现下这个`sleep`抽象方法
  sleep() {
    console.log("dog sleep");
  }
}

let dog = new Dog("wangwang");
// 在子类中调用下抽象类中的 eat 方法
dog.eat();

// 多态
class Cat extends Animal {
  sleep(): void {
    console.log("cat sleep");
  }
}

let cat = new Cat();

let animal: Animal[] = [dog, cat];
animal.forEach((item) => {
  item.sleep(); // 在程序执行的时候在这一处就会判断具体的实例是哪一种实例然后执行不同的方法，这样就实现了多态
});

// 特殊的TS类型：也就是this类型

// 类的成员方法呢可以直接返回一个 this 这样就可以很方便的实现链式调用
// 在继承的时候呢 this 类型也可以表现出多态，这里的多态是指 this 即可以是父类型也可以是子类型

class WorkFlow {
  step1() {
    return this;
  }
  step2() {
    return this;
  }
}

new WorkFlow().step1().step2(); // 方法的链式调用

class MyFlow extends WorkFlow {
  next() {
    return this;
  }
}
// 这个方法返回了子类的类型，那么这时候它也可以是父类类型
// 这样就保持了父类和子类之间接口调用的连贯性
new MyFlow().next().step1().step2();

export default {};
